package com.qf.mybatis03.utils;

import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;

import java.io.IOException;
import java.io.InputStream;

/*
    封装web环境下的sqlSession工具类
    1.如果在web环境下使用该工具类,最好可以让每个线程在调用getSqlSession方法时,能够获取到属于该线程自己的同一个getSqlSession对象
    2.避免重复创建对象

    该工具类提供哪些方法?
    1)获取SqlSession对象   2)提交事务   3)回滚事务   4)额外封装一个关闭资源的方法   5)返回指定类型的Dao层接口对象

    该工具类再提供一个方法,用于获取指定类型的Dao层接口的实现类对象
    T getMapper(Class<T> clazz)
 */
public class MybatisUtils {
    //类级别的成员变量SqlSessionFactory 好处:全局唯一 也是避免重复创建对象
    private static SqlSessionFactory sqlSessionFactory;
    //创建ThreadLocal<T>对象,该对象用于保存线程私有的数据
    private static ThreadLocal<SqlSession> threadLocal = new ThreadLocal<SqlSession>();

    //通过静态代码块初始化类级别的成员变量 仅在jvm加载class时执行一次
    static{
        try {
            InputStream inputStream = Resources.getResourceAsStream("mybatis-config.xml");
            SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
            sqlSessionFactory = sqlSessionFactoryBuilder.build(inputStream);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    //获取SqlSession对象*************
    public static SqlSession getSqlSession() {
        //先检查当前线程是否曾经获取过(创建过)SqlSession对象,如果获取过,那么直接使用
        SqlSession sqlSession = threadLocal.get();
        if (sqlSession != null){
            return sqlSession;
        }
        //如果当前线程没有获取过SqlSession对象，那么创建一个新的SqlSession对象，并且保存起来
        //该方法每次调用都会新创建一个SqlSession对象
        sqlSession = sqlSessionFactory.openSession();
        //把新创建的SqlSession对象保存到threadLocal对象中
        threadLocal.set(sqlSession);
        return sqlSession;
    }

    //提交事务***********
    public static void commit(){
        //获取当前线程的SqlSession对象
        SqlSession sqlSession = getSqlSession();
        sqlSession.commit();
        //严谨些：关闭当前线程的SqlSession对象
        closeSqlSession();
    }

    //回滚事务***********
    public static void rollback(){
        //获取当前线程的SqlSession对象
        SqlSession sqlSession = getSqlSession();
        sqlSession.rollback();
        //严谨些：关闭当前线程的SqlSession对象
        closeSqlSession();
    }

    //关闭SqlSession
    public static void closeSqlSession() {
        //获取当前线程的SqlSession对象
        SqlSession sqlSession = getSqlSession();
        //清理掉threadLocal对象中曾经保存过的sqlSession对象
        threadLocal.remove();
        //关闭
        sqlSession.close();
    }

    //返回指定类型的Dao层接口对象
    public static <T> T getMapper(Class<T> clazz){
        //获取当前线程的sqlsession对象
        SqlSession sqlSession = getSqlSession();
        T mapper = sqlSession.getMapper(clazz);
        return mapper;
    }
}

